package zhongbo.javaweb.instagram.schedule;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import zhongbo.javaweb.instagram.model.Config;
import zhongbo.javaweb.instagram.model.Proxy;
import zhongbo.javaweb.instagram.service.model.ConfigService;
import zhongbo.javaweb.instagram.service.model.ProxyService;
import zhongbo.javaweb.instagram.util.DateFormatUtil;
import zhongbo.javaweb.instagram.util.HttpClientUtil;
import zhongbo.javaweb.instagram.util.StrUtil;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static zhongbo.javaweb.instagram.util.PerformanceUtil.countEndStr;
import static zhongbo.javaweb.instagram.util.PerformanceUtil.countStart;

/**
 * 说明：
 *
 * @author zhongboWork
 * @version 1.0
 * @since 2021-12-20 17:53
 */
@Component
@Slf4j
@Data
public class ProxyCheckSchedule {

    @Autowired
    private ProxyService proxyService;

    @Autowired
    private ConfigService configService;
    private List<Proxy> proxyList = new ArrayList<>();

    private List<String> availableProxyList = new ArrayList<>();

    private List<String> banedProxyList = new ArrayList<>();

    @Value("${proxy.check.enabled}")
    private boolean enabled = true;

    @Value("${proxy.check.url}")
    private String proxyCheckUrl = "https://api-ipv4.ip.sb/ip";
    @Value("${proxy.check.timeOutMs}")
    private int proxyCheckTimeOutMs = 10000;
    @Value("${proxy.check.intervalMs}")
    private long proxyCheckIntervalMs = 30000;
    @Value("${proxy.check.unBanIntervalMs}")
    private long proxyUnBanIntervalMs = TimeUnit.DAYS.toMillis(2);

    private String availableProxy;

    private long lastCheckTime;
    private long lastUnBanTime;

    @PostConstruct
    public void initProxy() {
        this.proxyList = proxyService.findAll();
    }

    //@Scheduled(fixedDelay = 1000)
    public void checkProxyList() {
        if (System.currentTimeMillis() - lastCheckTime > proxyCheckIntervalMs) {
            lastCheckTime = System.currentTimeMillis();
            if (!enabled) {
                return;
            }
            log.info("======== 代理检查开始... 检查间隔: {}", DateFormatUtil.formatMs(proxyCheckIntervalMs));
            countStart();
            refreshProxy();
            if (this.proxyList.size() > 0) {
                for (Proxy proxy : this.proxyList) {
                    if (proxy.isEnabled()) {
                        doCheckProxy(proxy);
                        proxyService.addOrUpdate(proxy);
                    }
                }
                updateAvailableProxy();
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("未配置代理地址列表");
                }
            }
            lastCheckTime = System.currentTimeMillis();
            String countEndStr = countEndStr();
            log.info("代理检查完毕, 有效代理: {}; 当前代理: {}; 耗时 {}", availableProxyList, availableProxy, countEndStr);
        }

    }

    private void refreshProxy() {
        proxyList = proxyService.findAll();
    }

    private void updateAvailableProxy() {
        if (availableProxyList.isEmpty()) {
            availableProxy = null;
        } else {
            if (availableProxyList.contains(availableProxy)) {
                log.info("当前代理地址: {} (未更换)", availableProxy);
                return;
            }
            int next = availableProxyList.indexOf(availableProxy) + 1;
            if (next >= availableProxyList.size()) {
                next = 0;
            }
            String nextProxy = availableProxyList.get(next);
            if (!nextProxy.equals(availableProxy)) {
                log.info("当前代理地址: {}, 上个代理地址: {}", nextProxy, availableProxy);
                availableProxy = nextProxy;
            } else {
                log.info("当前代理地址: {}", availableProxy);
            }
        }
    }

    private void doCheckProxy(Proxy proxy) {
        HttpGet httpGet = null;
        String proxyUrl = null;
        try {
            proxyUrl = proxy.getUrl();
            if (StringUtils.isEmpty(proxyUrl)) {
                proxy.setAvailable(false);
                log.warn("无效的代理信息,url空! {}", proxy);
                return;
            }
            if (checkIsBan(proxyUrl)) {
                proxy.setBaned(true);
                proxy.setBanedTime(System.currentTimeMillis());
                return;
            } else {
                proxy.setBaned(false);
                proxy.setBanedTime(0);
            }
            // HttpClient 代理检测
            HttpHost proxyHost = HttpHost.create(proxyUrl);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setProxy(proxyHost)
                    .setConnectTimeout(proxyCheckTimeOutMs)
                    .setRedirectsEnabled(false)
                    .build();
            CloseableHttpClient httpClient = HttpClientUtil.chooseInsClient();
            Config config = configService.get();
            String proxyCheckUrl = config.getProxyCheckUrl();
            if (StringUtils.isEmpty(proxyCheckUrl)) {
                proxyCheckUrl = this.proxyCheckUrl;
            }
            httpGet = new HttpGet(proxyCheckUrl);
            httpGet.setConfig(requestConfig);
            CloseableHttpResponse execute = httpClient.execute(httpGet);
            String body = EntityUtils.toString(execute.getEntity());
            // http://192.168.2.126:8118 body: 101.32.33.8 20211215
            int statusCode = execute.getStatusLine().getStatusCode();
            boolean statusSuccess = execute.getStatusLine().getStatusCode() == 200;
            log.info("检测代理成功! {} 代理响应 statusCode: {}, success: {}, body: {}", proxyUrl, statusCode, statusSuccess, StrUtil.subStr(body, 0, 100));
            if (statusSuccess) {
                proxy.setAvailable(true);
                if (!availableProxyList.contains(proxyUrl)) {
                    availableProxyList.add(proxyUrl);
                }
                return;
            }
        } catch (IOException e) {
            log.error("检测代理地址失败, 读取信息失败! httpGet: {} error: {}", httpGet, e);
        } catch (IllegalArgumentException e) {
            log.error("检测代理地址失败, 读取信息失败! proxyUrl: {} error: {}", proxyUrl, e);
        } catch (Exception e) {
            log.error("检测代理地址失败, 读取信息失败! proxyUrl: {} errorException: {}", proxyUrl, e);
        }
        proxy.setAvailable(false);
        availableProxyList.remove(proxyUrl);
    }

    private boolean checkIsBan(String proxyUrl) {
        long banPast = System.currentTimeMillis() - lastUnBanTime;
        if (banPast > proxyUnBanIntervalMs) {
            log.info("到达解封周期{}, 解 ban 所有代理地址 {}", DateFormatUtil.formatMs(proxyUnBanIntervalMs), banedProxyList);
            banedProxyList.clear();
        }
        if (banedProxyList.contains(proxyUrl)) {
            if (log.isDebugEnabled()) {
                log.debug("代理地址: {} 已被封", proxyUrl);
            }
            availableProxyList.remove(proxyUrl);
            return true;
        }
        return false;
    }

    public void banProxy(String proxyUrl) {
        if (!StringUtils.isEmpty(proxyUrl)) {
            banedProxyList.add(proxyUrl);
        }
    }
}
